#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#include "gui/gui.h"
#include "utils_string.h"

typedef struct TextViewData
{
    char *text_ori;
    char *text_buf;
    int text_ori_w;
    int text_ori_h;
    uint32_t text_color;
    int single_line;
    int text_scoll_enabled;
    int text_x;
    int text_y;
    int text_x_off;
    int text_scoll_count;
} TextViewData;

static int TextViewDestroyData(TextViewData *data)
{
    if (!data)
        return -1;

    if (data->text_ori)
        free(data->text_ori);
    if (data->text_buf)
        free(data->text_buf);
    free(data);
    return 0;
}

int TextViewMeasure(TextView *textView)
{
    if (!textView)
        return -1;

    TextViewData *textData = (TextViewData *)textView->viewdata;
    LayoutParams *textParams = &textView->params;

    if (textParams->available_w <= 0 || textParams->available_h <= 0)
    {
        textView->measured_w = 0;
        textView->measured_h = 0;
        return -1;
    }

    int layout_max_w = textParams->available_w - textParams->margin_left - textParams->margin_right;
    int layout_max_h = textParams->available_h - textParams->margin_top - textParams->margin_bottom;
    int layout_wrap_w = textData->text_ori_w + textView->padding_left + textView->padding_right;
    int layout_wrap_h = textData->text_ori_h + textView->padding_top + textView->padding_bottom;

    textView->wrap_w = layout_wrap_w;

    if (textParams->layout_w == TYPE_LAYOUT_SIZE_MATH_PARENT)
        textView->measured_w = layout_max_w;
    else if (textParams->layout_w == TYPE_LAYOUT_SIZE_WRAP_CONTENT)
        textView->measured_w = layout_wrap_w;
    else
        textView->measured_w = textParams->layout_w;
    if (textView->measured_w > layout_max_w)
        textView->measured_w = layout_max_w;
    if (textView->measured_w < 0)
        textView->measured_w = 0;

    if (textData->text_buf)
    {
        free(textData->text_buf);
        textData->text_buf = NULL;
    }

    if (textData->text_ori)
    {
        int text_max_w = textView->measured_w - textView->padding_left - textView->padding_right;

        if (text_max_w > 0 && textData->text_ori_w > text_max_w)
        {
            if (textData->single_line)
                textData->text_buf = StringMakeShortByWidth(textData->text_ori, text_max_w);
            else
                textData->text_buf = StringBreakLineByWidth(textData->text_ori, text_max_w);
            layout_wrap_h = GUI_GetTextHeight(textData->text_buf) + textView->padding_top + textView->padding_bottom;
        }
    }

    textView->wrap_h = layout_wrap_h;

    if (textParams->layout_h == TYPE_LAYOUT_SIZE_MATH_PARENT)
        textView->measured_h = layout_max_h;
    else if (textParams->layout_h == TYPE_LAYOUT_SIZE_WRAP_CONTENT)
        textView->measured_h = layout_wrap_h;
    else
        textView->measured_h = textParams->layout_h;
    if (textView->measured_h > layout_max_h)
        textView->measured_h = layout_max_h;
    if (textView->measured_h < 0)
        textView->measured_h = 0;

    return 0;
}

int TextViewDraw(TextView *textView)
{
    if (!textView)
        return -1;

    TextViewData *textData = (TextViewData *)textView->viewdata;
    LayoutParams *textParams = &textView->params;

    if (textView->measured_w <= 0 || textView->measured_h <= 0)
        return 0;

    int layout_x = textParams->layout_x + textParams->margin_left;
    int layout_y = textParams->layout_y + textParams->margin_top;
    int layout_w = textView->measured_w;
    int layout_h = textView->measured_h;

    if (textView->bg_color)
        GUI_DrawFillRectangle(layout_x, layout_y, layout_w, layout_h, textView->bg_color);

    if (textData->text_ori)
    {
        int text_x = layout_x + textView->padding_left + textData->text_x;
        int text_y = layout_y + textView->padding_top + textData->text_y;
        int text_max_w = layout_w - textView->padding_left - textView->padding_right;
        int text_max_h = layout_h - textView->padding_top; // - textView->padding_bottom;

        if (textView->wrap_w < textView->measured_w)
        {
            if (textView->gravity & TYPE_LAYOUT_GRAVITY_RIGHT)
                text_x += (textView->measured_w - textView->wrap_w);
            else if (textView->gravity & TYPE_LAYOUT_GRAVITY_LEFT)
                ;
            else if (textView->gravity & TYPE_LAYOUT_GRAVITY_CENTER_HORIZONTAL)
                text_x += (textView->measured_w - textView->wrap_w) / 2;
        }
        if (textView->wrap_h < textView->measured_h)
        {
            if (textView->gravity & TYPE_LAYOUT_GRAVITY_BOTTOM)
                text_y += (textView->measured_h - textView->wrap_h);
            else if (textView->gravity & TYPE_LAYOUT_GRAVITY_TOP)
                ;
            else if (textView->gravity & TYPE_LAYOUT_GRAVITY_CENTER_VERTICAL)
                text_x += (textView->measured_h - textView->wrap_h) / 2;
        }

        const char *text = textData->text_buf;
        uint32_t color = textData->text_color;

        GUI_SetClipping(text_x, text_y, text_max_w, text_max_h);

        if (textView->is_focused && textData->text_scoll_enabled && textData->text_ori_w > text_max_w)
        {
            text = textData->text_ori;

            if (textData->text_scoll_count < 60)
            {
                textData->text_x_off = 0;
            }
            else if (textData->text_scoll_count < textData->text_ori_w + 60)
            {
                textData->text_x_off--;
            }
            else if (textData->text_scoll_count < textData->text_ori_w + 90)
            {
                color = (color & 0x00FFFFFF) | ((((color >> 24) * (textData->text_scoll_count - textData->text_ori_w - 60)) / 30) << 24); // fade-in in 0.5s
                textData->text_x_off = 0;
            }
            else
            {
                textData->text_scoll_count = 0;
            }

            textData->text_scoll_count++;
            text_x += textData->text_x_off;
        }
        else
        {
            textData->text_scoll_count = 0;
            textData->text_x_off = 0;
        }

        if (!text)
            text = textData->text_ori;
        if (text)
            GUI_DrawText(text_x, text_y, color, text);

        GUI_UnsetClipping();
    }

    return 0;
}

int TextViewSetText(TextView *textView, const char *text)
{
    if (!textView || !textView->viewdata)
        return -1;

    TextViewData *textData = (TextViewData *)textView->viewdata;

    if (textData->text_ori)
    {
        free(textData->text_ori);
        textData->text_ori = NULL;
    }
    if (textData->text_buf)
    {
        free(textData->text_buf);
        textData->text_buf = NULL;
    }

    textData->text_ori_w = 0;
    textData->text_ori_h = 0;

    if (text)
    {
        textData->text_ori = malloc(strlen(text) + 1);
        if (textData->text_ori)
        {
            strcpy(textData->text_ori, text);
            textData->text_ori_w = GUI_GetTextWidth(textData->text_ori);
            textData->text_ori_h = GUI_GetTextHeight(textData->text_ori);
            if (textData->text_ori_h <= 0)
                textData->text_ori_h = GUI_GetLineHeight();
        }
    }

    return 0;
}

const char *TextViewGetText(TextView *textView)
{
    if (!textView || !textView->viewdata)
        return NULL;

    TextViewData *textData = (TextViewData *)textView->viewdata;
    return textData->text_ori;
}

int TextViewSetTextColor(TextView *textView, uint32_t color)
{
    if (!textView || !textView->viewdata)
        return -1;

    TextViewData *textData = (TextViewData *)textView->viewdata;
    textData->text_color = color;
    return 0;
}

uint32_t TextViewGetTextColor(TextView *textView)
{
    if (!textView || !textView->viewdata)
        return 0;

    TextViewData *textData = (TextViewData *)textView->viewdata;
    return textData->text_color;
}

int TextViewSetSingleLine(TextView *textView, int single_line)
{
    if (!textView || !textView->viewdata)
        return -1;

    TextViewData *textData = (TextViewData *)textView->viewdata;
    textData->single_line = single_line;
    return 0;
}

int TextViewIsSingleLine(TextView *textView)
{
    if (!textView || !textView->viewdata)
        return 0;

    TextViewData *textData = (TextViewData *)textView->viewdata;
    return textData->single_line;
}

int TextViewSetTextScollEnabled(TextView *textView, int enabled)
{
    if (!textView || !textView->viewdata)
        return -1;

    TextViewData *textData = (TextViewData *)textView->viewdata;
    textData->text_scoll_enabled = enabled;
    return 0;
}

int TextViewIsTextScollEnabled(TextView *textView)
{
    if (!textView || !textView->viewdata)
        return 0;

    TextViewData *textData = (TextViewData *)textView->viewdata;
    return textData->text_scoll_enabled;
}

int TextViewInit(TextView *textView)
{
    if (!textView)
        return -1;

    memset(textView, 0, sizeof(TextView));

    textView->viewdata = calloc(1, sizeof(TextViewData));
    if (!textView->viewdata)
        return -1;

    textView->onMeasure = TextViewMeasure;
    textView->onDraw = TextViewDraw;
    textView->onFinish = TextViewDestroy;

    return 0;
}

int TextViewDeinit(TextView *textView)
{
    if (!textView)
        return -1;

    TextViewDestroyData((TextViewData *)textView->viewdata);
    memset(textView, 0, sizeof(TextView));

    return 0;
}

TextView *NewTextView()
{
    TextView *textView = (TextView *)malloc(sizeof(TextView));
    if (!textView)
        return NULL;

    if (TextViewInit(textView) < 0)
    {
        free(textView);
        return NULL;
    }

    return textView;
}

int TextViewDestroy(TextView *textView)
{
    if (!textView)
        return -1;

    if (textView->dont_free)
        return 0;

    TextViewDestroyData((TextViewData *)textView->viewdata);
    free(textView);

    return 0;
}
